﻿using FPSO.HMI.Utils;
using FPSO.Models.Constant;
using FPSO.Models.Domain;
using FPSO.Models.ViewModel.OPC;
using FPSO.Models.ViewModel.TaskManage;
using FPSO.Services;
using FPSO.Services.Impl;
using System;
using System.Collections.Generic;
using System.Linq;

namespace FPSO.HMI.Task
{
    /// <summary>
    /// 装载任务执行器
    /// </summary>
    public class LoadTaskExecuter
    {
        /// <summary>
        /// 货油舱指令列表
        /// </summary>
        private List<InstructLog> COTInstructList;

        /// <summary>
        /// 压载舱指令列表
        /// </summary>
        private List<InstructLog> WBTInstructList;

        /// <summary>
        /// 任务服务
        /// </summary>
        private ITankTaskService TaskService;

        /// <summary>
        /// 命令服务
        /// </summary>
        private IInstructService InstructService;

        private ShipStateService ShipService;

        /// <summary>
        /// 队列ID
        /// </summary>
        private int QueueId;

        private OPCUtil opcUtil;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="taskQueue"></param>
        /// <param name="taskService"></param>
        /// <param name="instructService"></param>
        public LoadTaskExecuter(int taskQueue, ITankTaskService taskService, IInstructService instructService)
        {
            QueueId = taskQueue;
            TaskService = taskService;
            COTInstructList = new List<InstructLog>();
            WBTInstructList = new List<InstructLog>();
            InstructService = instructService;
            ShipService = new ShipStateService(SysConfig.PDBConnStr, "tab_int");

            opcUtil = new OPCUtil();
        }

        /// <summary>
        /// 初始化任务队列，暂未实现
        /// </summary>
        public void InitTaskList()
        {

        }

        /// <summary>
        /// 定时执行的工作
        /// </summary>
        public void Worker()
        {
            //1.检查是否有定时运行的任务，如果有则启动运行
            TaskTimer();
            //2.监控指令是否被下位机接收，如果被下位机接收则更新指令状态
            MonitorInstruct();
			//3.监控任务是否执行完成,
			MonitorTask();
		}

        /// <summary>
        /// 启动定时任务
        /// 查询是否有当前需要执行的任务，如果有则
        /// </summary>
        private void TaskTimer()
        {
			List<TankTask> taskList = TaskService.GetAllWaitTaskList();
            foreach(TankTask task in taskList)
            {
                if (task.StartType == TaskStartTypeConstant.START_BY_TIME_ID)
                {
                    //定时启动
					if(DateTime.Now >= task.PlanStartTime)
                    //if (task.ExecStartTime > DateTime.Now 
                    //    && task.ExecStartTime < DateTime.Now.AddMinutes(10))
                    {
                        MakeInstruct(task);
                    }
                }
            }
        }

        /// <summary>
        /// 监控指令是否被接收
        /// </summary>
        private void MonitorInstruct()
        {
            //监控指令是否被接收，若被接收则更新指令状态，并继续发送新的指令到下位机
            List<InstructLog> cotInsList = COTInstructList.Where(i => i.InsState == 1).ToList();
            if (cotInsList.Count() > 0)
            {
                List<OPCTaskInstruct> opcTaskList = opcUtil.GetTaskInstructCache(QueueId, true);
                foreach(OPCTaskInstruct ins in opcTaskList)
                {
                    if (ins.ReadFlag == 2)
                    {
                        InstructLog insLog = cotInsList.First(i => i.TaskId == ins.TkNo);
                        if (insLog != null)
                        {
                            //指令已经被下位机接收，更新状态
                            insLog.InsState = 2;
                            insLog.UpdateTime = DateTime.Now;
                            InstructService.UpdateInstructLog(insLog);
                        }
                    }
                }
            }

            List<InstructLog> wbtInsList = WBTInstructList.Where(i => i.InsState == 1).ToList();
            if (wbtInsList.Count() > 0)
            {
                List<OPCTaskInstruct> opcTaskList = opcUtil.GetTaskInstructCache(QueueId, false);
                foreach(OPCTaskInstruct ins in opcTaskList)
                {
                    if (ins.ReadFlag == 2)
                    {
                        InstructLog insLog = wbtInsList.First(i => i.TaskId == ins.TkNo);
                        if (insLog != null)
                        {
                            //指令已经被下位机接收，更新状态
                            insLog.InsState = 2;
                            insLog.UpdateTime = DateTime.Now;
                            InstructService.UpdateInstructLog(insLog);
                        }
                    }
                }
            }

            //对于过期的指令，设置过期状态
            foreach(InstructLog ins in cotInsList)
            {
                if (ins.InsState == 1 && ins.CreateTime < DateTime.Now.AddMinutes(-10))
                {
                    ins.InsState = 11;
                    ins.UpdateTime = DateTime.Now;
                    InstructService.UpdateInstructLog(ins);
                    TankTask tankTask = TaskService.GetTask(ins.TaskId);
                    if (tankTask != null)
                    {
                        tankTask.TaskResult = TaskResultConstant.FAILED_ID;
                        tankTask.UpdateTime = DateTime.Now;
                        TaskService.UpdateTask(tankTask);
                    }
                }
            }

            foreach(InstructLog ins in wbtInsList)
            {
                if (ins.InsState == 1 && ins.CreateTime < DateTime.Now.AddMinutes(-10))
                {
                    ins.InsState = 11;
                    ins.UpdateTime = DateTime.Now;
                    InstructService.UpdateInstructLog(ins);
                    TankTask tankTask = TaskService.GetTask(ins.TaskId);
                    if (tankTask != null)
                    {
                        tankTask.TaskResult = TaskResultConstant.FAILED_ID;
                        tankTask.UpdateTime = DateTime.Now;
                        TaskService.UpdateTask(tankTask);
                    }
                }
            }

            //如果当前没有正在执行的命令，并且存在需要下发的命令，则执行下发命令操作
            if (COTInstructList.Where(ins=>ins.InsState == 1).Count() == 0)
            {
                //当前没有正在执行的指令
                //找3条指令下发
                List<InstructLog> downInsList = COTInstructList.Where(ins => ins.InsState == 0).Take(3).ToList();
                if (downInsList.Count > 0)
                {
                    foreach(InstructLog insLog in downInsList)
                    {
                        insLog.InsState = 1;
                        InstructService.UpdateInstructLog(insLog);
                    }
                    opcUtil.SaveTaskInstructCache(QueueId, true, downInsList);
                }
            }

            if (WBTInstructList.Where(ins=>ins.InsState == 1).Count() == 0)
            {
                List<InstructLog> downInsList = COTInstructList.Where(ins => ins.InsState == 0).Take(3).ToList();
                if (downInsList.Count > 0)
                {
                    foreach(InstructLog insLog in downInsList)
                    {
                        insLog.InsState = 1;
                        InstructService.UpdateInstructLog(insLog);
                    }
                    opcUtil.SaveTaskInstructCache(QueueId, false, downInsList);
                }
            }

            //将执行失败，或执行成功的指令清除
            cotInsList = cotInsList.Where(ins => ins.InsState < 4).ToList();
            wbtInsList = wbtInsList.Where(ins => ins.InsState < 4).ToList();
        }

        /// <summary>
        /// 监控任务是否执行完成
        /// </summary>
        private void MonitorTask()
        {
            ShipTankSnapState shipSnapState =  ShipService.GetShipTankSnapState();
            foreach (InstructLog ins in COTInstructList)
            {
                TankStates tankStates = shipSnapState.GetTankStates(ins.TankId);
                if (tankStates != null)
                {
                    if (ins.InsState == 2)
                    {
                        //todo:还需要优化
                        if (Math.Abs(tankStates.Level.Value - ins.TargetLevel) < 2)
                        {
                            //执行下一个任务，并设置指令执行状态
                            ins.InsState = 3;
                            TaskAlmostComplete(ins.TaskId);
                            InstructService.UpdateInstructLog(ins);
                        }
                    }
                    else if (ins.InsState == 3)
                    {
                        //todo:还需要优化
                        if (Math.Abs(tankStates.Level.Value - ins.TargetLevel) < 1)
                        {
                            //任务执行完成
                            ins.InsState = 4;
                            TaskComplete(ins.TaskId);
                            InstructService.UpdateInstructLog(ins);
                        }
                    }
                }
            }
            foreach(InstructLog ins in WBTInstructList)
            {
                TankStates tankStates = shipSnapState.GetTankStates(ins.TankId);
                if (tankStates != null)
                {
                    if (ins.InsState == 2)
                    {
                        //todo:还需要优化
                        if (Math.Abs(tankStates.Level.Value - ins.TargetLevel) < 2)
                        {
                            //执行下一个任务，并设置指令执行状态
                            ins.InsState = 3;
                            TaskAlmostComplete(ins.TaskId);
                            InstructService.UpdateInstructLog(ins);
                        }
                    }
                    else if (ins.InsState == 3)
                    {
                        //todo:还需要优化
                        if (Math.Abs(tankStates.Level.Value - ins.TargetLevel) < 1)
                        {
                            //任务执行完成
                            ins.InsState = 4;
                            TaskComplete(ins.TaskId);
                            InstructService.UpdateInstructLog(ins);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 创建一个指令，定时任务或者顺序执行的任务
        /// </summary>
        private void MakeInstruct(TankTask task)
        {
            InstructLog insLog = new InstructLog();
            insLog.TaskId = task.TaskId;
            insLog.TaskQueue = QueueId;
            insLog.InsState = 0;
            insLog.TankId = task.TankId;
            insLog.TargetLevel = task.TargetVolm; // todo: 这里需要调整, 从容量百分比到液位高
            insLog.TaskType = task.TaskType == TaskTypeConstant.LOAD_ID ? 1 : 2;
            insLog.TankType = TankConstant.TankIdIsCOT(task.TankId) ? 0 : 1;
            insLog.TaskOp = task.TaskType == TaskTypeConstant.LOAD_ID ? 1 : 2;
            insLog.CreateTime = DateTime.Now;
            insLog.SendTime = DateTime.MinValue;
            insLog.UpdateTime = DateTime.Now;

            if (TankConstant.TankIdIsCOT(task.TankId))
            {
                COTInstructList.Add(insLog);
            }
            else
            {
                WBTInstructList.Add(insLog);
            }

            InstructService.AddInstructLog(insLog);
            task.UpdateTime = DateTime.Now;
            task.ExecStartTime = DateTime.Now;
            task.TaskState = TaskStateConstant.EXEC_ID;
            TaskService.UpdateTask(task);
        }

        /// <summary>
        /// 任务即将完成
        /// 从任务队列中查询是否有下一个任务，如果有则启动下一个任务
        /// </summary>
        /// <param name="taskId">任务ID</param>
        private void TaskAlmostComplete(int taskId)
        {
            List<TankTask> taskList = TaskService.GetWaitingTaskList(QueueId);
            List<TankTask> nextTaskList = taskList.Where(task => task.PreTaskId == taskId).ToList();
            foreach(TankTask task in nextTaskList)
            {
                MakeInstruct(task);
            }
        }

        /// <summary>
        /// 任务执行完成
        /// </summary>
        /// <param name="taskId"></param>
        private void TaskComplete(int taskId)
        {
            TankTask task = TaskService.GetTask(taskId);
            if (task != null)
            {
                task.TaskState = TaskStateConstant.COMPLETE_ID;
                TaskService.UpdateTask(task);
            }
        }
    }
}
